package game;

import java.util.ArrayList;
import java.util.Random;

import game.Config.Config;

import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Vector2f;
import org.newdawn.slick.tiled.TiledMap;
/**
 * This class represents a level in the game.
 * A level in the game loads a TiledMap from the given String.
 * The level will also generate node graphs based on the levels layout.
 * @author Sebastian Isheden and Victor Beer.
 *
 */
public class Level {
	
	private TiledMap tileMap;
	private ArrayList<NodeGraph> nodeGraphs = new ArrayList<NodeGraph>();
	/**
	 * Constructor for the Level. Initializes the TiledMap based on given String
	 * and creates all the node graphs contained in the TiledMap.
	 * @param tileSetName  The name of the file that we want to load from(Tmx format).
	 * @throws SlickException  If the given file doesn't exist.
	 */
	public Level(String tileSetName) throws SlickException{
		tileMap = new TiledMap(tileSetName, "");
		this.createNodeGraphs();
	}
	/**
	 * Draws the map with the left top corner at (positionX, positionY).
	 */
	public void draw(){
		tileMap.render(0, 0, Config.BACKGROUND_LAYER_ID);
	}
	/**
	 * @return The TiledMap this level is based on.
	 */
	public TiledMap getTileMap(){
		return this.tileMap;
	}
	/**
	 * This method returns a random path through the level.
	 * @return	Return the path we want to use.
	 */
	public NodeGraph getRandomPath(){
		Random random = new Random();
		return this.nodeGraphs.get(random.nextInt(this.nodeGraphs.size()));
	}
	//This method will go through all the "edges" of the map and check if it contains a start node.
	private void createNodeGraphs() {
		for(int x = 0; x < tileMap.getWidth(); x++) {
			if(tileMap.getTileId(x, 0, Config.NODE_LAYER_ID) == Config.START_NODE_ID) {
				appendGraph(x,0);
			}
			if(tileMap.getTileId(x, tileMap.getHeight()-1, Config.NODE_LAYER_ID) == Config.START_NODE_ID) {
				appendGraph(x, tileMap.getHeight() - 1);
			}
		}
		
		for(int y = 1; y < tileMap.getHeight() - 1; y++) {
			if(tileMap.getTileId(0, y, Config.NODE_LAYER_ID) == Config.START_NODE_ID) {
				appendGraph(0,y);
			}
			
			if(tileMap.getTileId(tileMap.getWidth()-1,y, Config.NODE_LAYER_ID) == Config.START_NODE_ID) {
				appendGraph(tileMap.getWidth() - 1,y);
			}
		}
	}
	//When a start is found, this method finds the end of the path and generates a graph from it.
	private void appendGraph(int x ,int y) {
		NodeGraph graph = new NodeGraph(new Vector2f(x, y));
		Vector2f previousNode = new Vector2f(-1,-1);
		Vector2f node = new Vector2f(x,y);
		
		Vector2f vectorUp = new Vector2f(0,-1);
		Vector2f vectorRight = new Vector2f(1,0);
		Vector2f vectorDown = new Vector2f(0,1);
		Vector2f vectorLeft = new Vector2f(-1,0);
		
		while(tileMap.getTileId((int)node.x, (int)node.y, Config.NODE_LAYER_ID) != Config.END_NODE_ID){
			if(isAllowedNode(node.copy().add(vectorUp)) && !node.copy().add(vectorUp).equals(previousNode)) {
				graph.addNode(node.copy().add(vectorUp));
				previousNode = node.copy();
				node = node.copy().add(vectorUp);
			} else if(isAllowedNode(node.copy().add(vectorRight)) && !node.copy().add(vectorRight).equals(previousNode)) {
				graph.addNode(node.copy().add(vectorRight));
				previousNode = node.copy();
				node = node.copy().add(vectorRight);
			} else if(isAllowedNode(node.copy().add(vectorDown)) && !node.copy().add(vectorDown).equals(previousNode)) {
				graph.addNode(node.copy().add(vectorDown));
				previousNode = node.copy();
				node = node.copy().add(vectorDown);
			} else if(isAllowedNode(node.copy().add(vectorLeft)) && !node.copy().add(vectorLeft).equals(previousNode)) {
				graph.addNode(node.copy().add(vectorLeft));
				previousNode = node.copy();
				node = node.copy().add(vectorLeft);
			}
		}
		
		this.nodeGraphs.add(graph);
	}
	
	private boolean isAllowedNode(Vector2f node){
		int x = (int)node.x;
		int y = (int)node.y;
		if(x < 0 || x >= tileMap.getWidth()) {
			return false;
		}
		if(y < 0 || y >= tileMap.getHeight())  {
			return false;
		}

		return isNodeTile(x,y);
	}
	
	private boolean isNodeTile(int x, int y) {
		if(tileMap.getTileId(x, y, Config.NODE_LAYER_ID) == Config.NODE_ID ||
		   tileMap.getTileId(x, y, Config.NODE_LAYER_ID) == Config.END_NODE_ID) {
			return true;
		}
		return false;
	}
	

}
